#include "wx/wxprec.h"
#include "wx/filename.h"
#include "wx/private/filename.h"
#include "wx/tokenzr.h"
#include "wx/config.h"
#include "wx/dynlib.h"

#ifdef __UNIX_LIKE__
#include <sys/types.h>
#include <utime.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

#ifdef __DJGPP__
#include <unistd.h>
#endif

#ifdef __MWERKS__
#ifdef __MACH__
#include <sys/types.h>
#include <utime.h>
#include <sys/stat.h>
#include <unistd.h>
#else
#include <stat.h>
#include <unistd.h>
#include <unix.h>
#endif
#endif

#ifdef __WATCOMC__
#include <io.h>
#include <sys/utime.h>
#include <sys/stat.h>
#endif

#ifdef __VISAGECPP__
#ifndef MAX_PATH
#define MAX_PATH 256
#endif
#endif

#ifdef __EMX__
#include <os2.h>
#define MAX_PATH _MAX_PATH
#endif

wxULongLong wxInvalidSize = ( unsigned ) - 1;

static wxString wxGetVolumeString( const wxString& volume, wxPathFormat format ) {
  wxString path;
  if( !volume.empty() ) {
    format = wxFileName::GetFormat( format );
    if( format == wxPATH_DOS && volume.length() > 1 ) {
      path << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << volume;
    } else if( format == wxPATH_DOS || format == wxPATH_VMS ) {
      path << volume << wxFileName::GetVolumeSeparator( format );
    }
  }
  return path;
}

inline bool IsDOSPathSep( wxChar ch ) {
  return ch == wxFILE_SEP_PATH_DOS || ch == wxFILE_SEP_PATH_UNIX;
}

static bool IsUNCPath( const wxString& path, wxPathFormat format ) {
  return format == wxPATH_DOS && path.length() >= 4 && IsDOSPathSep( path[0u] ) &&
         IsDOSPathSep( path[1u] ) && !IsDOSPathSep( path[2u] );
}

void wxFileName::Assign( const wxFileName &filepath ) {
  if( &filepath == this ) {
    return;
  }
  m_volume = filepath.GetVolume();
  m_dirs = filepath.GetDirs();
  m_name = filepath.GetName();
  m_ext = filepath.GetExt();
  m_relative = filepath.m_relative;
  m_hasExt = filepath.m_hasExt;
}

void wxFileName::Assign( const wxString& volume, const wxString& path, const wxString& name,
                         const wxString& ext, bool hasExt, wxPathFormat format ) {
  if( IsUNCPath( path, format ) ) {
    wxString pathNonUNC( path, 1, wxString::npos );
    SetPath( pathNonUNC, format );
  } else {
    SetPath( path, format );
  }
  m_volume = volume;
  m_ext = ext;
  m_name = name;
  m_hasExt = hasExt;
}

void wxFileName::SetPath( const wxString& pathOrig, wxPathFormat format ) {
  m_dirs.Clear();
  if( pathOrig.empty() ) {
    m_relative = true;
    return;
  }
  format = GetFormat( format );
  wxString volume, path;
  SplitVolume( pathOrig, &volume, &path, format );
  if( !volume.empty() ) {
    m_relative = false;
    SetVolume( volume );
  }
  wxChar leadingChar = path[0u];
  switch( format ) {
    case wxPATH_MAC:
      m_relative = leadingChar == wxT( ':' );
      if( m_relative ) {
        path.erase( 0, 1 );
      }
      break;
    case wxPATH_VMS:
      m_relative = false;
      break;
    default:
      wxFAIL_MSG( _T( "Unknown path format" ) );
    case wxPATH_UNIX:
      m_relative = leadingChar != wxT( '/' ) && leadingChar != _T( '~' );
      break;
    case wxPATH_DOS:
      m_relative = !IsPathSeparator( leadingChar, format );
      break;
  }
  wxStringTokenizer tn( path, GetPathSeparators( format ) );
  while( tn.HasMoreTokens() ) {
    wxString token = tn.GetNextToken();
    if( token.empty() ) {
      if( format == wxPATH_MAC ) {
        m_dirs.Add( wxT( ".." ) );
      }
    } else {
      m_dirs.Add( token );
    }
  }
}

void wxFileName::Assign( const wxString& fullpath, wxPathFormat format ) {
  wxString volume, path, name, ext;
  bool hasExt;
  SplitPath( fullpath, &volume, &path, &name, &ext, &hasExt, format );
  Assign( volume, path, name, ext, hasExt, format );
}

void wxFileName::Assign( const wxString& fullpathOrig, const wxString& fullname, wxPathFormat format ) {
  wxString fullpath = fullpathOrig;
  if( !fullpath.empty() && !wxEndsWithPathSeparator( fullpath ) ) {
    fullpath += GetPathSeparator( format );
  }
  wxString volume, path, name, ext;
  bool hasExt;
  SplitPath( fullname, NULL, NULL, &name, &ext, &hasExt, format );
  SplitPath( fullpath, &volume, &path, NULL, NULL, format );
  Assign( volume, path, name, ext, hasExt, format );
}

void wxFileName::Assign( const wxString& pathOrig, const wxString& name, const wxString& ext, wxPathFormat format ) {
  wxString volume, path;
  SplitVolume( pathOrig, &volume, &path, format );
  Assign( volume, path, name, ext, format );
}

void wxFileName::AssignDir( const wxString& dir, wxPathFormat format ) {
  Assign( dir, wxEmptyString, format );
}

void wxFileName::Clear() {
  m_dirs.Clear();
  m_volume = m_name = m_ext = wxEmptyString;
  m_relative = true;
  m_hasExt = false;
}

wxFileName wxFileName::FileName( const wxString& file, wxPathFormat format ) {
  return wxFileName( file, format );
}

wxFileName wxFileName::DirName( const wxString& dir, wxPathFormat format ) {
  wxFileName fn;
  fn.AssignDir( dir, format );
  return fn;
}

bool wxFileName::FileExists() const {
  return wxFileName::FileExists( GetFullPath() );
}

bool wxFileName::FileExists( const wxString &file ) {
  return ::wxFileExists( file );
}

bool wxFileName::DirExists() const {
  return wxFileName::DirExists( GetPath() );
}

bool wxFileName::DirExists( const wxString &dir ) {
  return ::wxDirExists( dir );
}

void wxFileName::AssignCwd( const wxString& volume ) {
  AssignDir( wxFileName::GetCwd( volume ) );
}

wxString wxFileName::GetCwd( const wxString& volume ) {
  wxString cwdOld;
  if( !volume.empty() ) {
    cwdOld = wxGetCwd();
    SetCwd( volume + GetVolumeSeparator() );
  }
  wxString cwd = ::wxGetCwd();
  if( !volume.empty() ) {
    SetCwd( cwdOld );
  }
  return cwd;
}

bool wxFileName::SetCwd() {
  return wxFileName::SetCwd( GetPath() );
}

bool wxFileName::SetCwd( const wxString &cwd ) {
  return ::wxSetWorkingDirectory( cwd );
}

void wxFileName::AssignHomeDir() {
  AssignDir( wxFileName::GetHomeDir() );
}

wxString wxFileName::GetHomeDir() {
  return ::wxGetHomeDir();
}

#if wxUSE_FILE || wxUSE_FFILE

#if !defined wx_fdopen && defined HAVE_FDOPEN
#define wx_fdopen fdopen
#endif

#define wxOPEN_EXCL O_EXCL

#ifdef wxOpenOSFHandle
#define WX_HAVE_DELETE_ON_CLOSE
// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags.
//
static int wxOpenWithDeleteOnClose( const wxString& filename ) {
  DWORD access = GENERIC_READ | GENERIC_WRITE;
  DWORD disposition = OPEN_ALWAYS;
  DWORD attributes = FILE_ATTRIBUTE_TEMPORARY |
                     FILE_FLAG_DELETE_ON_CLOSE;
  HANDLE h = ::CreateFile( filename, access, 0, NULL,
                           disposition, attributes, NULL );
  return wxOpenOSFHandle( h, wxO_BINARY );
}
#endif // wxOpenOSFHandle

static int wxTempOpen( const wxString& path, bool *deleteOnClose ) {
  #ifdef WX_HAVE_DELETE_ON_CLOSE
  if( *deleteOnClose ) {
    return wxOpenWithDeleteOnClose( path );
  }
  #endif
  *deleteOnClose = false;
  return wxOpen( path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600 );
}


#if wxUSE_FFILE
// Helper to open the file and attach it to the wxFFile
//
static bool wxTempOpen( wxFFile *file, const wxString& path, bool *deleteOnClose ) {
  #ifndef wx_fdopen
  *deleteOnClose = false;
  return file->Open( path, _T( "w+b" ) );
  #else // wx_fdopen
  int fd = wxTempOpen( path, deleteOnClose );
  if( fd == -1 ) {
    return false;
  }
  file->Attach( wx_fdopen( fd, "w+b" ) );
  return file->IsOpened();
  #endif // wx_fdopen
}
#endif // wxUSE_FFILE


#if !wxUSE_FILE
#define WXFILEARGS(x, y) y
#elif !wxUSE_FFILE
#define WXFILEARGS(x, y) x
#else
#define WXFILEARGS(x, y) x, y
#endif

static wxString wxCreateTempImpl(
  const wxString& prefix,
  WXFILEARGS( wxFile *fileTemp, wxFFile *ffileTemp ),
  bool *deleteOnClose = NULL ) {
  #if wxUSE_FILE && wxUSE_FFILE
  wxASSERT( fileTemp == NULL || ffileTemp == NULL );
  #endif
  wxString path, dir, name;
  bool wantDeleteOnClose = false;
  if( deleteOnClose ) {
    // set the result to false initially
    wantDeleteOnClose = *deleteOnClose;
    *deleteOnClose = false;
  } else {
    // easier if it alwasys points to something
    deleteOnClose = &wantDeleteOnClose;
  }
  // 使用前缀指定的目录
  wxFileName::SplitPath( prefix, &dir, &name, NULL );
  if( dir.empty() ) {
    dir = wxFileName::GetTempDir();
  }
  path = dir;
  if( !wxEndsWithPathSeparator( dir ) && ( name.empty() || !wxIsPathSeparator( name[0u] ) ) ) {
    path += wxFILE_SEP_PATH;
  }
  path += name;
  path += _T( "XXXXXX" );
  wxCharBuffer buf( wxConvFile.cWX2MB( path ) );
  // cast is safe because the string length doesn't change
  int fdTemp = mkstemp( ( char* )( const char* ) buf );
  if( fdTemp == -1 ) {
    // this might be not necessary as mkstemp() on most systems should have
    // already done it but it doesn't hurt neither...
    path.clear();
  } else { // mkstemp() succeeded
    path = wxConvFile.cMB2WX( ( const char* ) buf );
    #if wxUSE_FILE
    // avoid leaking the fd
    if( fileTemp ) {
      fileTemp->Attach( fdTemp );
    } else
    #endif
    #if wxUSE_FFILE
      if( ffileTemp ) {
        #ifdef wx_fdopen
        ffileTemp->Attach( wx_fdopen( fdTemp, "r+b" ) );
        #else
        ffileTemp->Open( path, _T( "r+b" ) );
        close( fdTemp );
        #endif
      } else
    #endif
      {
        close( fdTemp );
      }
  }
  if( path.empty() ) {
    wxLogSysError( _( "无法创建临时文件名" ) );
  } else {
    bool ok = true;
    #if wxUSE_FILE
    if( fileTemp && !fileTemp->IsOpened() ) {
      *deleteOnClose = wantDeleteOnClose;
      int fd = wxTempOpen( path, deleteOnClose );
      if( fd != -1 ) {
        fileTemp->Attach( fd );
      } else
      { ok = false; }
    }
    #endif
    #if wxUSE_FFILE
    if( ffileTemp && !ffileTemp->IsOpened() ) {
      *deleteOnClose = wantDeleteOnClose;
      ok = wxTempOpen( ffileTemp, path, deleteOnClose );
    }
    #endif
    if( !ok ) {
      wxLogError( _( "打开临时文件失败." ) );
      path.clear();
    }
  }
  return path;
}

static bool wxCreateTempImpl(
  const wxString& prefix,
  WXFILEARGS( wxFile *fileTemp, wxFFile *ffileTemp ),
  wxString *name ) {
  bool deleteOnClose = true;
  *name = wxCreateTempImpl( prefix, WXFILEARGS( fileTemp, ffileTemp ), &deleteOnClose );
  bool ok = !name->empty();
  if( deleteOnClose ) {
    name->clear();
  } else if( ok && wxRemoveFile( *name ) ) {
    name->clear();
  }
  return ok;
}

static void wxAssignTempImpl(
  wxFileName *fn,
  const wxString& prefix,
  WXFILEARGS( wxFile *fileTemp, wxFFile *ffileTemp ) ) {
  wxString tempname;
  tempname = wxCreateTempImpl( prefix, WXFILEARGS( fileTemp, ffileTemp ) );
  if( tempname.empty() ) {
    fn->Clear();
  } else {
    fn->Assign( tempname );
  }
}

void wxFileName::AssignTempFileName( const wxString& prefix ) {
  wxAssignTempImpl( this, prefix, WXFILEARGS( NULL, NULL ) );
}

wxString wxFileName::CreateTempFileName( const wxString& prefix ) {
  return wxCreateTempImpl( prefix, WXFILEARGS( NULL, NULL ) );
}

#endif // wxUSE_FILE || wxUSE_FFILE


#if wxUSE_FILE

wxString wxCreateTempFileName( const wxString& prefix, wxFile *fileTemp, bool *deleteOnClose ) {
  return wxCreateTempImpl( prefix, WXFILEARGS( fileTemp, NULL ), deleteOnClose );
}

bool wxCreateTempFile( const wxString& prefix, wxFile *fileTemp, wxString *name ) {
  return wxCreateTempImpl( prefix, WXFILEARGS( fileTemp, NULL ), name );
}

void wxFileName::AssignTempFileName( const wxString& prefix, wxFile *fileTemp ) {
  wxAssignTempImpl( this, prefix, WXFILEARGS( fileTemp, NULL ) );
}

wxString wxFileName::CreateTempFileName( const wxString& prefix, wxFile *fileTemp ) {
  return wxCreateTempFileName( prefix, fileTemp );
}

#endif // wxUSE_FILE


#if wxUSE_FFILE

wxString wxCreateTempFileName( const wxString& prefix, wxFFile *fileTemp, bool *deleteOnClose ) {
  return wxCreateTempImpl( prefix, WXFILEARGS( NULL, fileTemp ), deleteOnClose );
}

bool wxCreateTempFile( const wxString& prefix, wxFFile *fileTemp, wxString *name ) {
  return wxCreateTempImpl( prefix, WXFILEARGS( NULL, fileTemp ), name );
}

void wxFileName::AssignTempFileName( const wxString& prefix, wxFFile *fileTemp ) {
  wxAssignTempImpl( this, prefix, WXFILEARGS( NULL, fileTemp ) );
}

wxString wxFileName::CreateTempFileName( const wxString& prefix, wxFFile *fileTemp ) {
  return wxCreateTempFileName( prefix, fileTemp );
}

#endif // wxUSE_FFILE

wxString wxFileName::GetTempDir() {
  wxString dir;
  dir = wxGetenv( _T( "TMPDIR" ) );
  if( dir.empty() ) {
    dir = wxGetenv( _T( "TMP" ) );
    if( dir.empty() ) {
      dir = wxGetenv( _T( "TEMP" ) );
    }
  }
  if( dir.empty() ) {
    dir = _T( "/tmp" );
  }
  return dir;
}

bool wxFileName::Mkdir( int perm, int flags ) {
  return wxFileName::Mkdir( GetPath(), perm, flags );
}

bool wxFileName::Mkdir( const wxString& dir, int perm, int flags ) {
  if( flags & wxPATH_MKDIR_FULL ) {
    wxFileName filename;
    filename.AssignDir( dir );
    wxString currPath;
    if( filename.HasVolume() ) {
      currPath << wxGetVolumeString( filename.GetVolume(), wxPATH_NATIVE );
    }
    wxArrayString dirs = filename.GetDirs();
    size_t count = dirs.GetCount();
    for( size_t i = 0; i < count; i++ ) {
      if( i > 0 || filename.IsAbsolute() ) {
        currPath += wxFILE_SEP_PATH;
      }
      currPath += dirs[i];
      if( !DirExists( currPath ) ) {
        if( !wxMkdir( currPath, perm ) ) {
          return false;
        }
      }
    }
    return true;
  }
  return ::wxMkdir( dir, perm );
}

bool wxFileName::Rmdir() {
  return wxFileName::Rmdir( GetPath() );
}

bool wxFileName::Rmdir( const wxString &dir ) {
  return ::wxRmdir( dir );
}

bool wxFileName::Normalize( int flags, const wxString& cwd, wxPathFormat format ) {
  if( flags & wxPATH_NORM_ENV_VARS ) {
    wxString pathOrig = GetFullPath( format );
    wxString path = wxExpandEnvVars( pathOrig );
    if( path != pathOrig ) {
      Assign( path );
    }
  }
  wxArrayString dirs = GetDirs();
  wxFileName curDir;
  format = GetFormat( format );
  if( ( flags & wxPATH_NORM_ABSOLUTE ) && !IsAbsolute( format ) ) {
    if( cwd.empty() ) {
      curDir.AssignCwd( GetVolume() );
    } else {
      curDir.AssignDir( cwd );
    }
  }
  if( ( format == wxPATH_UNIX ) && ( flags & wxPATH_NORM_TILDE ) ) {
    if( !dirs.IsEmpty() ) {
      wxString dir = dirs[0u];
      if( !dir.empty() && dir[0u] == _T( '~' ) ) {
        curDir.AssignDir( wxGetUserHome( dir.c_str() + 1 ) );
        m_relative = true;
        dirs.RemoveAt( 0u );
      }
    }
  }
  if( curDir.IsOk() ) {
    if( !HasVolume() && curDir.HasVolume() ) {
      SetVolume( curDir.GetVolume() );
      if( !m_relative ) {
        curDir.Clear();
      }
    }
    wxArrayString dirsNew = curDir.GetDirs();
    WX_PREPEND_ARRAY( dirs, dirsNew );
    if( curDir.IsAbsolute( format ) ) {
      m_relative = false;
    }
  }
  m_dirs.Empty();
  size_t count = dirs.GetCount();
  for( size_t n = 0; n < count; n++ ) {
    wxString dir = dirs[n];
    if( flags & wxPATH_NORM_DOTS ) {
      if( dir == wxT( "." ) ) {
        continue;
      }
      if( dir == wxT( ".." ) ) {
        if( m_dirs.IsEmpty() ) {
          wxLogError( _( "The path '%s' contains too many \"..\"!" ), GetFullPath().c_str() );
          return false;
        }
        m_dirs.RemoveAt( m_dirs.GetCount() - 1 );
        continue;
      }
    }
    m_dirs.Add( dir );
  }
  if( ( flags & wxPATH_NORM_CASE ) && !IsCaseSensitive( format ) ) {
    m_volume.MakeLower();
    m_name.MakeLower();
    m_ext.MakeLower();
    count = m_dirs.GetCount();
    for( size_t i = 0; i < count; i++ ) {
      m_dirs[i].MakeLower();
    }
  }
  return true;
}

bool wxFileName::IsAbsolute( wxPathFormat format ) const {
  if( m_relative ) {
    return false;
  }
  if( !GetVolumeSeparator( format ).empty() ) {
    if( GetVolume().empty() ) {
      return false;
    }
  }
  return true;
}

bool wxFileName::MakeRelativeTo( const wxString& pathBase, wxPathFormat format ) {
  wxFileName fnBase = wxFileName::DirName( pathBase, format );
  wxString cwd = wxGetCwd();
  Normalize( wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format );
  fnBase.Normalize( wxPATH_NORM_ALL & ~wxPATH_NORM_CASE, cwd, format );
  bool withCase = IsCaseSensitive( format );
  if( !GetVolume().IsSameAs( fnBase.GetVolume(), withCase ) ) {
    return false;
  }
  m_volume.clear();
  while( !m_dirs.IsEmpty() && !fnBase.m_dirs.IsEmpty() &&
         m_dirs[0u].IsSameAs( fnBase.m_dirs[0u], withCase ) ) {
    m_dirs.RemoveAt( 0 );
    fnBase.m_dirs.RemoveAt( 0 );
  }
  size_t count = fnBase.m_dirs.GetCount();
  for( size_t i = 0; i < count; i++ ) {
    m_dirs.Insert( wxT( ".." ), 0u );
  }
  if( format == wxPATH_UNIX || format == wxPATH_DOS ) {
    if( m_dirs.IsEmpty() && IsDir() ) {
      m_dirs.Add( _T( '.' ) );
    }
  }
  m_relative = true;
  return true;
}

bool wxFileName::SameAs( const wxFileName& filepath, wxPathFormat format ) const {
  wxFileName fn1 = *this, fn2 = filepath;
  wxString cwd = wxGetCwd();
  fn1.Normalize( wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format );
  fn2.Normalize( wxPATH_NORM_ALL | wxPATH_NORM_CASE, cwd, format );
  if( fn1.GetFullPath() == fn2.GetFullPath() ) {
    return true;
  }
  return false;
}

bool wxFileName::IsCaseSensitive( wxPathFormat format ) {
  return GetFormat( format ) == wxPATH_UNIX;
}

wxString wxFileName::GetForbiddenChars( wxPathFormat format ) {
  wxString strForbiddenChars = wxT( "*?" );
  wxCOMPILE_TIME_ASSERT( wxPATH_MAX == 5, wxPathFormatChanged );
  switch( GetFormat( format ) ) {
    default :
      wxFAIL_MSG( wxT( "Unknown path format" ) );
    case wxPATH_UNIX:
      break;
    case wxPATH_MAC:
      strForbiddenChars = wxEmptyString;
      break;
    case wxPATH_DOS:
      strForbiddenChars += wxT( "\\/:\"<>|" );
      break;
    case wxPATH_VMS:
      break;
  }
  return strForbiddenChars;
}

wxString wxFileName::GetVolumeSeparator( wxPathFormat ( format ) ) {
  wxString sepVol;
  if( ( GetFormat( format ) == wxPATH_DOS ) ||
      ( GetFormat( format ) == wxPATH_VMS ) ) {
    sepVol = wxFILE_SEP_DSK;
  }
  return sepVol;
}

wxString wxFileName::GetPathSeparators( wxPathFormat format ) {
  wxString seps;
  switch( GetFormat( format ) ) {
    case wxPATH_DOS:
      seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX;
      break;
    default:
      wxFAIL_MSG( _T( "Unknown wxPATH_XXX style" ) );
    case wxPATH_UNIX:
      seps = wxFILE_SEP_PATH_UNIX;
      break;
    case wxPATH_MAC:
      seps = wxFILE_SEP_PATH_MAC;
      break;
    case wxPATH_VMS:
      seps = wxFILE_SEP_PATH_VMS;
      break;
  }
  return seps;
}

wxString wxFileName::GetPathTerminators( wxPathFormat format ) {
  format = GetFormat( format );
  return format == wxPATH_VMS ? wxString( _T( ']' ) ) : GetPathSeparators( format );
}

bool wxFileName::IsPathSeparator( wxChar ch, wxPathFormat format ) {
  return ch != _T( '\0' ) && GetPathSeparators( format ).Find( ch ) != wxNOT_FOUND;
}

bool wxFileName::IsValidDirComponent( const wxString& dir ) {
  if( dir.empty() ) {
    wxFAIL_MSG( _T( "传递到的空目录 wxFileName::InsertDir()" ) );
    return false;
  }
  const size_t len = dir.length();
  for( size_t n = 0; n < len; n++ ) {
    if( dir[n] == GetVolumeSeparator() || IsPathSeparator( dir[n] ) ) {
      wxFAIL_MSG( _T( "中的目录组件无效 wxFileName" ) );
      return false;
    }
  }
  return true;
}

void wxFileName::AppendDir( const wxString& dir ) {
  if( IsValidDirComponent( dir ) ) {
    m_dirs.Add( dir );
  }
}

void wxFileName::PrependDir( const wxString& dir ) {
  InsertDir( 0, dir );
}

void wxFileName::InsertDir( size_t before, const wxString& dir ) {
  if( IsValidDirComponent( dir ) ) {
    m_dirs.Insert( dir, before );
  }
}

void wxFileName::RemoveDir( size_t pos ) {
  m_dirs.RemoveAt( pos );
}

void wxFileName::SetFullName( const wxString& fullname ) {
  SplitPath( fullname, NULL, NULL, &m_name, &m_ext, &m_hasExt );
}

wxString wxFileName::GetFullName() const {
  wxString fullname = m_name;
  if( m_hasExt ) {
    fullname << wxFILE_SEP_EXT << m_ext;
  }
  return fullname;
}

wxString wxFileName::GetPath( int flags, wxPathFormat format ) const {
  format = GetFormat( format );
  wxString fullpath;
  if( flags & wxPATH_GET_VOLUME ) {
    fullpath += wxGetVolumeString( GetVolume(), format );
  }
  switch( format ) {
    case wxPATH_MAC:
      if( m_relative ) {
        fullpath += wxFILE_SEP_PATH_MAC;
      }
      break;
    case wxPATH_DOS:
      if( !m_relative ) {
        fullpath += wxFILE_SEP_PATH_DOS;
      }
      break;
    default:
      wxFAIL_MSG( wxT( "Unknown path format" ) );
    case wxPATH_UNIX:
      if( !m_relative ) {
        if( m_dirs.IsEmpty() || m_dirs[0u] != _T( '~' ) ) {
          fullpath += wxFILE_SEP_PATH_UNIX;
        }
      }
      break;
    case wxPATH_VMS:
      flags &= ~wxPATH_GET_SEPARATOR;
  }
  if( m_dirs.empty() ) {
    return fullpath;
  }
  if( format == wxPATH_VMS ) {
    fullpath += wxT( '[' );
  }
  const size_t dirCount = m_dirs.GetCount();
  for( size_t i = 0; i < dirCount; i++ ) {
    switch( format ) {
      case wxPATH_MAC:
        if( m_dirs[i] == wxT( "." ) ) {
          continue;
        }
        if( !m_dirs[i].IsSameAs( wxT( ".." ) ) ) {
          fullpath += m_dirs[i];
        }
        break;
      default:
        wxFAIL_MSG( wxT( "Unexpected path format" ) );
      case wxPATH_DOS:
      case wxPATH_UNIX:
        fullpath += m_dirs[i];
        break;
      case wxPATH_VMS:
        if( !m_dirs[i].IsSameAs( wxT( ".." ) ) ) {
          fullpath += m_dirs[i];
        }
        break;
    }
    if( ( flags & wxPATH_GET_SEPARATOR ) || ( i != dirCount - 1 ) ) {
      fullpath += GetPathSeparator( format );
    }
  }
  if( format == wxPATH_VMS ) {
    fullpath += wxT( ']' );
  }
  return fullpath;
}

wxString wxFileName::GetFullPath( wxPathFormat format ) const {
  wxString fullpath = GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR, format );
  fullpath += GetFullName();
  return fullpath;
}

wxString wxFileName::GetShortPath() const {
  wxString path( GetFullPath() );
  return path;
}

wxString wxFileName::GetLongPath() const {
  wxString pathOut, path = GetFullPath();
  pathOut = path;
  return pathOut;
}

wxPathFormat wxFileName::GetFormat( wxPathFormat format ) {
  if( format == wxPATH_NATIVE ) {
    format = wxPATH_UNIX;
  }
  return format;
}

void wxFileName::SplitVolume( const wxString& fullpathWithVolume, wxString *pstrVolume, wxString *pstrPath, wxPathFormat format ) {
  format = GetFormat( format );
  wxString fullpath = fullpathWithVolume;
  if( IsUNCPath( fullpath, format ) ) {
    fullpath.erase( 0, 2 );
    size_t posFirstSlash =
      fullpath.find_first_of( GetPathTerminators( format ) );
    if( posFirstSlash != wxString::npos ) {
      fullpath[posFirstSlash] = wxFILE_SEP_DSK;
      fullpath.insert( posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS );
    }
  }
  if( format == wxPATH_DOS || format == wxPATH_VMS ) {
    wxString sepVol = GetVolumeSeparator( format );
    size_t posFirstColon = fullpath.find_first_of( sepVol );
    if( posFirstColon != wxString::npos ) {
      if( pstrVolume ) {
        *pstrVolume = fullpath.Left( posFirstColon );
      }
      fullpath.erase( 0, posFirstColon + sepVol.length() );
    }
  }
  if( pstrPath ) {
    *pstrPath = fullpath;
  }
}

void wxFileName::SplitPath( const wxString& fullpathWithVolume, wxString *pstrVolume, wxString *pstrPath, wxString *pstrName, wxString *pstrExt,
                            bool *hasExt, wxPathFormat format ) {
  format = GetFormat( format );
  wxString fullpath;
  SplitVolume( fullpathWithVolume, pstrVolume, &fullpath, format );
  size_t posLastDot = fullpath.find_last_of( wxFILE_SEP_EXT );
  size_t posLastSlash = fullpath.find_last_of( GetPathTerminators( format ) );
  if( ( posLastDot != wxString::npos ) && ( posLastDot == 0 || IsPathSeparator( fullpath[posLastDot - 1] ) ||
      ( format == wxPATH_VMS && fullpath[posLastDot - 1] == _T( ']' ) ) ) ) {
    posLastDot = wxString::npos;
  }
  if( ( posLastDot != wxString::npos ) && ( posLastSlash != wxString::npos ) && ( posLastDot < posLastSlash ) ) {
    posLastDot = wxString::npos;
  }
  if( pstrPath ) {
    if( posLastSlash == wxString::npos ) {
      pstrPath->Empty();
    } else {
      size_t len = posLastSlash;
      if( !len && format != wxPATH_MAC ) {
        len++;
      }
      *pstrPath = fullpath.Left( len );
      if( format == wxPATH_VMS ) {
        if( ( *pstrPath )[0u] == _T( '[' ) ) {
          pstrPath->erase( 0, 1 );
        }
      }
    }
  }
  if( pstrName ) {
    size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1;
    size_t count;
    if( posLastDot == wxString::npos ) {
      count = wxString::npos;
    } else if( posLastSlash == wxString::npos ) {
      count = posLastDot;
    } else {
      count = posLastDot - posLastSlash - 1;
    }
    *pstrName = fullpath.Mid( nStart, count );
  }
  if( posLastDot == wxString::npos ) {
    if( pstrExt ) {
      pstrExt->clear();
    }
    if( hasExt ) {
      *hasExt = false;
    }
  } else {
    if( pstrExt ) {
      *pstrExt = fullpath.Mid( posLastDot + 1 );
    }
    if( hasExt ) {
      *hasExt = true;
    }
  }
}

void wxFileName::SplitPath( const wxString& fullpath, wxString *path, wxString *name, wxString *ext, wxPathFormat format ) {
  wxString volume;
  SplitPath( fullpath, &volume, path, name, ext, format );
  if( path ) {
    path->Prepend( wxGetVolumeString( volume, format ) );
  }
}

wxString wxFileName::StripExtension( const wxString& fullpath ) {
  wxFileName fn( fullpath );
  fn.SetExt( _T( "" ) );
  return fn.GetFullPath();
}

bool wxFileName::SetTimes( const wxDateTime *dtAccess, const wxDateTime *dtMod, const wxDateTime *dtCreate ) {
  wxUnusedVar( dtCreate );
  if( !dtAccess && !dtMod ) {
    return true;
  }
  utimbuf utm;
  utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks();
  utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks();
  if( utime( GetFullPath().fn_str(), &utm ) == 0 ) {
    return true;
  }
  wxLogSysError( _( "未能修改的文件时间 '%s'" ), GetFullPath().c_str() );
  return false;
}

bool wxFileName::Touch() {
  if( utime( GetFullPath().fn_str(), NULL ) == 0 ) {
    return true;
  }
  wxLogSysError( _( "无法触摸文件 '%s'" ), GetFullPath().c_str() );
  return false;
}

static int wxStat( const char *file_name, wxStructStat *buf ) {
  return stat( file_name, buf );
}

bool wxFileName::GetTimes( wxDateTime *dtAccess, wxDateTime *dtMod, wxDateTime *dtCreate ) const {
  wxStructStat stBuf;
  if( wxStat( GetFullPath().fn_str(), &stBuf ) == 0 ) {
    if( dtAccess ) {
      dtAccess->Set( stBuf.st_atime );
    }
    if( dtMod ) {
      dtMod->Set( stBuf.st_mtime );
    }
    if( dtCreate ) {
      dtCreate->Set( stBuf.st_ctime );
    }
    return true;
  }
  wxLogSysError( _( "无法检索的文件时间 '%s'" ), GetFullPath().c_str() );
  return false;
}

wxULongLong wxFileName::GetSize( const wxString &filename ) {
  if( !wxFileExists( filename ) ) {
    return wxInvalidSize;
  }
  wxStructStat st;
  if( wxStat( filename, &st ) != 0 ) {
    return wxInvalidSize;
  }
  return wxULongLong( st.st_size );
}

wxString wxFileName::GetHumanReadableSize( const wxULongLong &bs, const wxString &nullsize, int precision ) {
  static const double KILOBYTESIZE = 1024.0;
  static const double MEGABYTESIZE = 1024.0 * KILOBYTESIZE;
  static const double GIGABYTESIZE = 1024.0 * MEGABYTESIZE;
  static const double TERABYTESIZE = 1024.0 * GIGABYTESIZE;
  if( bs == 0 || bs == wxInvalidSize ) {
    return nullsize;
  }
  double bytesize = bs.ToDouble();
  if( bytesize < KILOBYTESIZE ) {
    return wxString::Format( _( "%s B" ), bs.ToString().c_str() );
  }
  if( bytesize < MEGABYTESIZE ) {
    return wxString::Format( _( "%.*f kB" ), precision, bytesize / KILOBYTESIZE );
  }
  if( bytesize < GIGABYTESIZE ) {
    return wxString::Format( _( "%.*f MB" ), precision, bytesize / MEGABYTESIZE );
  }
  if( bytesize < TERABYTESIZE ) {
    return wxString::Format( _( "%.*f GB" ), precision, bytesize / GIGABYTESIZE );
  }
  return wxString::Format( _( "%.*f TB" ), precision, bytesize / TERABYTESIZE );
}

wxULongLong wxFileName::GetSize() const {
  return GetSize( GetFullPath() );
}

wxString wxFileName::GetHumanReadableSize( const wxString &failmsg, int precision ) const {
  return GetHumanReadableSize( GetSize(), failmsg, precision );
}
